package org.matsadaml.tool;

import net.sf.saxon.xqj.SaxonXQDataSource;

import java.io.*;
import java.text.DecimalFormat;
import java.util.Random;

import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQDataSource;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQResultSequence;
import javax.xml.xquery.XQStaticContext;

public class Menu {

	private String MatsadaPath;
	private String ProductGroup;
	private String ProductId;
	private FopShop Transformer;
	private Templates CachedSetProductIdXslt;
	private XQPreparedExpression ProductIndexPE;
	private XQPreparedExpression SubstanceIndexPE;
	private XQPreparedExpression SummaryIndexPE;

	public Menu(String matsadaPath) throws Exception {
		super();
		MatsadaPath = matsadaPath;
		Transformer = new FopShop(new File(MatsadaPath,
				"ResourceFiles/Customizations/customized_msds2fo.xsl"));

		// For this to work the Saxon jar must have been added in the Classpath
		System.setProperty("javax.xml.transform.TransformerFactory",
				"net.sf.saxon.TransformerFactoryImpl");
		File xsltFile = new File(MatsadaPath + "/System/Transformations",
				"SetProductId.xsl");
		Source xsltSource = new StreamSource(xsltFile);
		TransformerFactory transFact = TransformerFactory.newInstance();
		try {
			CachedSetProductIdXslt = transFact.newTemplates(xsltSource);
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		}
		// Create XQPreparedExpression for product, substance and summary index.
        XQDataSource ds = new SaxonXQDataSource();
        XQConnection conn;
		conn = ds.getConnection();
		// for Products
		File productQuery = new File(MatsadaPath + "/System/XQuery", "CreateProductIndex.xq");
		FileInputStream productQueryFile = new FileInputStream(productQuery);
		XQStaticContext context = conn.getStaticContext();
		context.setBaseURI(productQuery.toURI().toString());
		ProductIndexPE = conn.prepareExpression(productQueryFile, context);
		// for Substances
		File substanceQuery = new File(MatsadaPath + "/System/XQuery", "CreateSubstanceIndex.xq");
		FileInputStream substanceQueryFile = new FileInputStream(substanceQuery);
		SubstanceIndexPE = conn.prepareExpression(substanceQueryFile, context);
		// for Substances
		File summaryQuery = new File(MatsadaPath + "/System/XQuery", "CreateSummaryIndex.xq");
		FileInputStream summaryQueryFile = new FileInputStream(summaryQuery);
		SummaryIndexPE = conn.prepareExpression(summaryQueryFile, context);
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		Menu menu = new Menu(args[0]);
		if (args.length == 2) {
			menu.SetProductAndGroup(args[1]);
			menu.CreatePdf(menu.ProductId);
		}
		menu.MenuDialog();
	}

	public void SetProductAndGroup(String filePath) {
		File productFile = new File(filePath);
		File groupDir = new File(productFile.getParent());
		// File productDir = new File(groupDir.getParent());
		ProductGroup = groupDir.getName();
		ProductId = productFile.getName().replaceFirst(".xml", "");
	}
	
	public void MenuDialog() {
		int result = 0;
		do {
			System.out.println("=============================================");
			if (ProductGroup != null) {
				System.out.println("1. Select product group (current is "
						+ ProductGroup + ")");
			} else {
				System.out.println("1. Select product group");
			}
			if (ProductId != null) {
				System.out.println("2. Select product (current is " + ProductId
						+ ")");
			} else {
				System.out.println("2. Select product");
			}
			System.out.println("3. Create PDF for selected product");
			System.out.println("4. Create PDFs for selected product group");
			System.out.println("5. Update overview and product group indexes");
			System.out
					.println("6. Create a new product file in selected product group");
			System.out.println("7. Create a new product group");
			System.out.println("8. ");
			System.out.println("9. Quit");
			System.out.print("Enter menu selection (1-9): ");
			try {
				result = Integer.valueOf(ReadConsole().trim()).intValue();
				ExecuteMenuCommand(result);
			} catch (Exception e) {
				System.out.println("You must enter a number between 1 and 9");
			}
		} while (result != 9);
	}

	private String ReadConsole() {
		// open up standard input
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		String result = null;

		// read the input from the command-line; need to use try/catch with the
		// readLine() method
		try {
			result = br.readLine();
		} catch (IOException ioe) {
			System.out.println("IO error trying to read from console!");
			System.exit(1);
		}
		return result;
	}

	private void ExecuteMenuCommand(int command) throws Exception {

		// Switch construct
		switch (command) {
		case 1:
			ChangeProductGroup();
			break;
		case 2:
			ChangeProduct();
			break;
		case 3:
			CreatePdf(ProductId);
			break;
		case 4:
			CreatePdfsForProductGroup();
			break;
		case 5:
			GenerateIndexes();
			break;
		case 6:
			CreateNewProductXml();
			break;
		case 666:
			TransformAndRenameProductXmls("OldTestCases", "TestCases");
			break;
		case 7:
			CreateProductGroup();
			break;
		case 8:
			System.out.println("Option 8 selected");
			break;
		case 9:
			System.out.println("MatsadaML Tool exited");
			break;
		default:
			System.out.println("You must enter a number between 1 and 9");
			break; // This break is not really necessary
		}
	}

	private void ChangeProductGroup() {
		int countDirs = 0;
		int countItems = 0;
		int lookup[] = new int[200];
		File dir = new File(MatsadaPath, "Products");
		String items[] = dir.list();
		System.out.println("=============================================");
		for (String item : items) {
			countItems++;
			File i = new File(MatsadaPath + "/Products", item);
			if (i.isDirectory() && (!item.substring(0, 1).equals("."))) {
				countDirs++;
				lookup[countDirs] = countItems;
				System.out.println(countDirs + ". " + item);
			}
		}
		System.out.print("Enter number of the product group to be selected: ");
		int selection;
		try {
			selection = Integer.valueOf(ReadConsole().trim()).intValue();
			String newGroup = items[lookup[selection] - 1].trim();
			dir = new File(MatsadaPath + "/Products", newGroup);
			boolean exists = dir.exists();
			if (exists) {
				ProductGroup = newGroup;
				System.out.println("Current product group changed to "
						+ ProductGroup);
			} else {
				System.out.println("Product group " + newGroup
						+ " does not exist");
			}
		} catch (Exception e) {
			System.out.println("You must enter a number from the list");
		}
	}

	private void ChangeProduct() {
		System.out.println("=============================================");
		if (ProductGroup != null) {
			System.out.print("Enter id of product you want to change to: ");
			String newId = ReadConsole().trim();
			File file = new File(MatsadaPath + "/Products/" + ProductGroup,
					newId + ".xml");
			boolean exists = file.exists();
			if (exists) {
				ProductId = newId;
				System.out.println("Current product changed to " + ProductId);
			} else {
				System.out.println("Product " + newId
						+ " does not exist in product group " + ProductGroup);
			}
		} else {
			System.out.println("You must select a product group first");
		}
	}

	private void CreatePdf(String productId) throws Exception {
		System.out.println("=============================================");
		if (ProductGroup != null) {
			File xmlFile = new File(MatsadaPath, "/Products/" + ProductGroup + "/"
					+ productId + ".xml");
			File saveFile = new File(MatsadaPath, "/Products/" + ProductGroup
					+ "/pdf/" + productId + ".pdf");
			Transformer.TransformAndSave(xmlFile, saveFile);
			System.out.println("Pdf created for " + productId);
		} else {
			System.out.println("You must select a product group first");
		}
	}

	private void CreatePdfsForProductGroup() {
		System.out.println("=============================================");
		if (ProductGroup != null) {
			File group = new File(MatsadaPath + "/Products", ProductGroup);
			if (group.isDirectory()
					&& (!group.getName().substring(0, 1).equals("."))) {
				String files[] = group.list();
				for (String file : files) {
					File f = new File(MatsadaPath + "/Products/" + ProductGroup,
							file);
					if (f.isFile() && file.endsWith(".xml")) {
						try {
							CreatePdf(file.replace(".xml", ""));
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
				System.out.println("PDFs created for product group "
						+ ProductGroup);
			} else {
				System.out.println("PDFs could NOT be created for product group "
						+ ProductGroup);
			}
		} else {
			System.out.println("You must select a product group first");
		}
	}

	private void CreateNewProductXml() throws Exception {
		System.out.println("=============================================");
		if (ProductGroup != null) {
			String auto = "";
			do {
				System.out.print("Do you want auto generated IDs (for example P426-2657)? reply Y or N: ");
				auto = ReadConsole().trim().toUpperCase();
			} while (!(auto.equals("Y") || auto.equals("N")));
	
			boolean unique;
			String productId;
			do {
				unique = true;
				if (auto.equals("Y")) {
					Random random = new Random();
					int i = random.nextInt(999);
					DecimalFormat formatter = new DecimalFormat("000");
					String firstGroup = formatter.format(i);
					i = random.nextInt(9999);
					formatter = new DecimalFormat("0000");
					String secondGroup = formatter.format(i);
					productId = "P" + firstGroup + "-" + secondGroup;
				} else {
					System.out.print("Enter the ID you want to use for the new product: ");
					productId = ReadConsole().trim();
				}
				File file = new File(MatsadaPath + "/Products/" + ProductGroup,
						productId + ".xml");
				if (file.exists()) {
					if (auto.equals("Y")) {
						System.out.print("A product with id " + productId + " already exists");
					}
					unique = false;
				}
			} while (!unique);
	
			ProductId = productId;
	
			File inputFile = new File(MatsadaPath + "/System/Schema",
					"Product-template.xml");
			File outputFile = new File(MatsadaPath + "/Products/" + ProductGroup,
					productId + ".xml");
			Source inputSource = new StreamSource(inputFile);
			OutputStream out = new java.io.FileOutputStream(outputFile);
			out = new java.io.BufferedOutputStream(out);
			StreamResult result = new StreamResult(out);
	
			// Setup XSLT
			Transformer transformer = CachedSetProductIdXslt.newTransformer();
			transformer.setParameter("ProductId", ProductId);
			// Start XSLT transformation and send to out
			transformer.transform(inputSource, result);
			out.flush();
			out.close();
	
			System.out.println("Product xml created for " + productId);
		} else {
			System.out.println("You must select a product group first");
		}
	}

	private void TransformAndRenameProductXmls(String inDir, String outDir)
			throws Exception {

		System.out.println("=============================================");
		File originDir = new File(MatsadaPath + "/Products", inDir);
		String files[] = originDir.list();
		String name;

		for (String file : files) {
			File product = new File(MatsadaPath + "/Products/" + inDir, file);
			if (product.isFile() && product.getName().endsWith(".xml")) {
				boolean unique;
				String productId;
				do {
					unique = true;
					Random random = new Random();
					int i = random.nextInt(999);
					DecimalFormat formatter = new DecimalFormat("000");
					String firstGroup = formatter.format(i);
					i = random.nextInt(9999);
					formatter = new DecimalFormat("0000");
					String secondGroup = formatter.format(i);
					name = secondGroup;
					productId = "P" + firstGroup + "-" + secondGroup;
					File newfile = new File(
							MatsadaPath + "/Products/" + outDir, productId
									+ ".xml");
					if (newfile.exists()) {
						unique = false;
					}
				} while (!unique);

				File outputFile = new File(MatsadaPath + "/Products/" + outDir,
						productId + ".xml");
				Source inputSource = new StreamSource(product);
				OutputStream out = new java.io.FileOutputStream(outputFile);
				out = new java.io.BufferedOutputStream(out);
				StreamResult result = new StreamResult(out);

				// Setup XSLT
				File xsltFile = new File(MatsadaPath
						+ "/System/Transformations", "RenameProductXml.xsl");
				Source xsltSource = new StreamSource(xsltFile);
				TransformerFactory transFact = TransformerFactory.newInstance();
				Transformer transformer = transFact.newTransformer(xsltSource);
				transformer.setParameter("ProductId", productId);
				transformer.setParameter("Name", name);
				// Start XSLT transformation and send to out
				transformer.transform(inputSource, result);
				out.flush();
				out.close();
				System.out.println("Product xml created for " + productId);

			}
		}

	}

	private void CreateProductGroup() {
		System.out.println("=============================================");
		System.out.print("Enter name of new product group to be created: ");
		String newGroup = ReadConsole().trim();
		File dir = new File(MatsadaPath + "/Products", newGroup);
		boolean created = dir.mkdir();
		if (created) {
			ProductGroup = newGroup;
			File pdf = new File(MatsadaPath + "/Products/" + newGroup, "pdf");
			pdf.mkdir();
			// File batSource = new File(MatsadaPath + "/System/Tool", "_CreatePdf.bat");
			// File batDestination = new File(MatsadaPath + "/Products/" + newGroup, "_CreatePdf.bat");
			// CopyFile(batSource, batDestination);
			System.out.println("Product group " + ProductGroup + " created");
		} else {
			System.out.println("Error! Product group " + newGroup
					+ " could not be created");
		}
	}

	/*
	private void CopyFile(File from, File to) throws IOException {
		
		FileChannel input = null, output = null;
		try {
			input = new FileInputStream(from).getChannel();
			output = new FileOutputStream(to).getChannel();

			long size = input.size();
			MappedByteBuffer buf = input.map(FileChannel.MapMode.READ_ONLY, 0,
					size);

			output.write(buf);

		} finally {
			if (input != null)
				input.close();
			if (output != null)
				output.close();
		}
	}
	*/
	
	private void GenerateIndexes() {
		File dir = new File(MatsadaPath, "Products");
		String items[] = dir.list();
		String groupList = "";
		for (String item : items) {
			File i = new File(MatsadaPath + "/Products", item);
			if (i.isDirectory() && (!item.substring(0, 1).equals("."))) {
				try {
					// Create Product Index.
					ProductIndexPE.bindString(new QName("ProductGroup"), i.getName(), null);
			        XQResultSequence result = ProductIndexPE.executeQuery();
			        File index = new File(MatsadaPath + "/Products/" + i.getName(), "_index_products.xml");
			        OutputStream output = new java.io.FileOutputStream(index);
					output = new java.io.BufferedOutputStream(output);
					StreamResult stream = new StreamResult(output);
					result.writeSequenceToResult(stream);
			        output.flush();
			        output.close();
					// Create Substance Index.
			        SubstanceIndexPE.bindString(new QName("ProductGroup"), i.getName(), null);
			        result = SubstanceIndexPE.executeQuery();
			        index = new File(MatsadaPath + "/Products/" + i.getName(), "_index_substances.xml");
			        output = new java.io.FileOutputStream(index);
					output = new java.io.BufferedOutputStream(output);
					stream = new StreamResult(output);
					result.writeSequenceToResult(stream);
			        output.flush();
			        output.close();
			        System.out.println("Indexes created for product group "
							+ i.getName());
			        // Update groupList
			        groupList = groupList.concat(i.getName() + " ");
				} catch (XQException e) {
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		// Create Summary Index.
		try {
			SummaryIndexPE.bindString(new QName("ProductGroups"), groupList, null);
	        XQResultSequence result = SummaryIndexPE.executeQuery();
	        File index = new File(MatsadaPath, "summary.xml");
	        OutputStream output = new java.io.FileOutputStream(index);
			output = new java.io.BufferedOutputStream(output);
			StreamResult stream = new StreamResult(output);
			result.writeSequenceToResult(stream);
	        output.flush();
	        output.close();
		} catch (XQException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

}
